import Head from 'next/head';

<Head>
  <title>{'Best Practices (TypeScript) - Material React Table V3 Docs'}</title>
  <meta
    name="description"
    content="The Best Practices to follow with TypeScript when using Material React Table."
  />
</Head>

## MRT Best Practices

Here are some best practices to follow when using Material React Table. We'll cover Type-Safety (even if you are not using TypeScript) and how to best create re-usable MRT components.

### Stay Up-To-Date

Run this command in your terminal every few weeks to make sure you are using the latest version of Material React Table and MUI

```bash
npx npm-check-updates -u material-react-table @mui/material @mui/x-date-pickers @mui/icons-material @emotion/react @emotion/styled
```

### Type-Safety

TanStack Table itself is written in TypeScript, and Material React Table builds on top of its great type definitions for a best-in-class TypeScript experience.

If, however, you cannot use TypeScript in your project for some reason, checkout down below for [how to use JSDoc instead of TypeScript](#use-jsdoc-instead-of-typescript) to get the same type hints.

#### Is TypeScript Required?

No, TypeScript is not required to use Material React Table. You can just use JavaScript and everything will work just fine, but you will be missing out on a lot of great type hints and type safety that can help you build your app faster and with less bugs.

There are a couple of ways to still get type hints without TypeScript with the [`createMRTColumnHelper`](#createmrtcolumnhelper-utility) utility function or by using [JSDoc](#use-jsdoc-instead-of-typescript), so you can still get some of the benefits of type safety without TypeScript.

#### Defining TData Type

Material React Table makes use of generics to make working with your specific row data structures easier. You will see that most of the `MRT_*` types that you can use accept a `TData` generic.

Let's say that the data in your table is an array of users that looks like this:

```tsx
const data: User[] = [
  { id: 1, name: 'John', age: 23 },
  { id: 2, name: 'Alice', age: 17 },
  { id: 3, name: 'Bob', age: 32 },
];
```

Then your `TData` type can be defined as:

```tsx
export type User = {
  id: number;
  name: string;
  age: number;
};
```

You will often pass this `TData` type as a generic to the `MRT_*` types that you use so that you can get type hints for your specific data structure.

#### Define Your Columns With Type-Safety

Material React Table provides a couple of ways to define your columns with type safety. You can either simply use the `MRT_ColumnDef` type or use the new `createMRTColumnHelper` utility function.

#### MRT_ColumnDef Type

The most straightforward way to define your columns with type-safety is to just type your columns as `Array<MRT_ColumnDef<TData>>`.

```tsx
import {
  MaterialReactTable,
  useMaterialReactTable,
  type MRT_ColumnDef, // <--- import MRT_ColumnDef
} from 'material-react-table';
import { type User } from './types'; // <--- import your TData type from wherever you defined it

// define your columns, pass User as a generic to MRT_ColumnDef
const columns: Array<MRT_ColumnDef<User>> = [
  {
    accessorKey: 'id', //you should get type hints for all of your keys if you defined your TData type correctly
    header: 'ID',
    enableSorting: false, //you should get type hints for all possible column options that you can define here
  },
  {
    accessorKey: 'name',
    header: 'Name',
  },
  {
    accessorFn: (originalRow) => Number(originalRow.age), //you should also get type hints for your accessorFn
    header: 'Age',
    Cell: ({ cell }) => <span>{cell.getValue<number>()}</span>, //cell.getValue() will be typed as `unknown` by default, but you can pass a generic to get the correct type
    //see the createMRTColumnHelper example below for a better way to get type safety with cell.getValue()
  },
];
```

#### createMRTColumnHelper Utility

> New in V3 (After many requests)

Alternatively you can use the `createMRTColumnHelper` utility function to define your columns. This works the same way as the TanStack `createColumnHelper`.

Additional `TValue` type-safety is provided by using this utility. That means that when you call `cell.getValue()` in either a custom `Cell` render, or in any of the `mui*Props`, you will get the correct type for the data in that column instead of `unknown`.

```tsx
import {
  MaterialReactTable,
  useMaterialReactTable,
  createMRTColumnHelper, // <--- import createMRTColumnHelper
} from 'material-react-table';
import { type User } from './types'; // <--- import your TData type from wherever you defined it (if using TS)

const columnHelper = createMRTColumnHelper<User>(); // <--- pass your TData type as a generic to createMRTColumnHelper (if using TS)

//columns will be inferred as Array<MRT_ColumnDef<User>>
const columns = [
  //accessorKey as first argument, rest of column options as second argument
  columnHelper.accessor('name', {
    header: 'Last Name',
  }),
  //accessorFn as first argument, rest of column options as second argument
  columnHelper.accessor((row) => Number(row.age), {
    id; 'age', //id required for accessorFn
    header: 'Age',
    filterVariant: 'range-slider', //you should get type hints for all possible column options that you can define here
    Cell: ({ cell }) => <span>{cell.getValue()}</span>, //cell.getValue() will be typed as number instead of unknown
  }),
  //display column (no accessor needed)
  columnHelper.display({
    header: 'Contact',
    Cell: ({ row }) => (
      <Button onClick={() => sendEmail(row.original.email)}>Send Email</Button>
    ),
  }),
];
```

#### Use JSDoc instead of TypeScript

If you are in a situation where you are not able to install TypeScript in your project, you can technically do the same thing as up above in JavaScript using JSDoc.

```jsx
import {
  MaterialReactTable,
  useMaterialReactTable,
} from 'material-react-table';

//define TData type with JSDoc
/**
 * @typedef {Object} User
 * @property {number} id
 * @property {string} name
 * @property {number} age
 */

//import MRT_ColumnDef type with JSDoc
/**
 * @type {import('material-react-table').MRT_ColumnDef<User>[]}
 */
const columns = [
  {
    accessorKey: 'id', //you should get type hints for all of your keys if you defined your TData type correctly
    header: 'ID',
    enableSorting: false, //you should get type hints for all possible column options that you can define here
  },
  {
    accessorKey: 'name',
    header: 'Name',
  },
  {
    accessorFn: (originalRow) => Number(originalRow.age), //you should also get type hints for your accessorFn
    header: 'Age',
  },
];
```

### Re-Usable MRT Components

If you are going to have multiple tables in your app, chances are that you will want to make a re-usable component built on top of Material React Table. This is a good idea and good practice, but here are a few suggestions to maintain type safety with some TypeScript generics.

#### Re-usable Components or Options?

In my opinion, instead of creating a re-usable component, it is instead actually best to define your default options and share them between all of your tables.

##### Re-usable Default Options

In this example, we are simply creating a factory function that creates all of the default options that you want all of your tables to start with.

```ts
import { type MRT_RowData, type MRT_TableOptions } from 'material-react-table';

//define re-useable default table options for all tables in your app
export const getDefaultMRTOptions = <TData extends MRT_RowData>(): Partial<
  MRT_TableOptions<TData>
> => ({
  //list all of your default table options here
  enableGlobalFilter: false,
  enableRowPinning: true,
  initialState: { showColumnFilters: true },
  manualFiltering: true,
  manualPagination: true,
  manualSorting: true,
  muiTableHeadCellProps: {
    sx: { fontSize: '1.1rem' },
  },
  paginationDisplayMode: 'pages',
  //etc...
  defaultColumn: {
    //you can even list default column options here
  },
});
```

Then you can use these options in every new table that you create:

```tsx
import {
  MaterialReactTable,
  useMaterialReactTable,
  type MRT_ColumnDef,
} from 'material-react-table';
import { getDefaultMRTOptions } from './utils'; //your default options

interface User {
  id: number;
  name: string;
  age: number;
}

const defaultMRTOptions = getDefaultMRTOptions<User>(); //get your default options

export const OneOfYourTableComponents = () => {
  const columns: MRT_ColumnDef<User>[] = [
    //...
  ];

  const { data } = useQuery({
    //...
  });

  const table = useMaterialReactTable({
    ...defaultMRTOptions, //spread your default options
    columns,
    data,
    enableGlobalFilter: true, //override default options
    initialState: {
      ...defaultMRTOptions.initialState, //spread default initial state
      showColumnFilters: false, //override default initial state for just this table
    },
    //...
  });

  //you will have access to the entire table instance where you need it
  console.log(table.getState());

  return <MaterialReactTable table={table} />;
};
```

Doing it this way, you maintain 100% control of your table instance and any state that you are managing in each table component.

I believe this is by far the best way to work with Material React Table in your application code, and how I personally use it in my own apps.

##### Re-usable MRT Component

If you still want to just create a re-usable MRT component instead, you can do that too, of course. Here is a type-safe way to do that:

```tsx
import {
  MaterialReactTable,
  useMaterialReactTable,
  type MRT_ColumnDef,
  type MRT_RowData, //default shape of TData (Record<string, any>)
  type MRT_TableOptions,
} from 'material-react-table';

interface Props<TData extends MRT_RowData> extends MRT_TableOptions<TData> {
  columns: MRT_ColumnDef<TData>[];
  data: TData[];
}

export const CustomMRTTable = <TData extends MRT_RowData>({
  columns,
  data,
  ...rest
}: Props<TData>) => {
  const table = useMaterialReactTable({
    columns,
    data,
    //your custom table options...
    ...rest, //accept props to override default table options
  });

  return <MaterialReactTable table={table} />;
};
```

By using the `TData` generic correctly, you can maintain type-safety in your re-usable component that will adapt to different types of data you will have throughout your application.

Though, be aware that the weakness of this approach is that it will be more annoying to get access to the `table` instance or read table state where you need it.

When re-using your MRT table component, it will just look something like this:

```tsx
import { CustomMRTTable } from './CustomMRTTable';

const columns: MRT_ColumnDef<User>[] = [
  //...
];

export const YourComponent = () => {
  //no easy access to the table instance or table state here unless you manage all of the state in this component
  const [pagination, setPagination] = useState<MRT_PaginationState>({
    pageIndex: 0,
    pageSize: 10,
  });
  const [sorting, setSorting] = useState<MRT_SortingState[]>([]);
  //etc...

  const { data } = useQuery({
    //...
  });

  return (
    <CustomMRTTable
      columns={columns}
      data={data}
      enableRowPinning
      //manage states to get access to them
      onPaginationChange={setPagination}
      onSortingChange={setSorting}
      //etc...
      state={{
        pagination,
        sorting,
        //etc...
      }}
    />
  );
};
```

### Debugging Material React Table

Material React Table _(though really TanStack Table)_ can be a lot easier to debug than other data grid libraries. This is because you are often in charge of the state that you care about, and the entire table instance is available to you in your own scope if you are using the `useMaterialReactTable` hook. There are also some advanced TanStack Table Dev Tools that you can optionally install.

#### Console Logging from the Table Instance

When in doubt, console log it! There are a lot of things you can easily console log. Here are some examples:

##### Console Log All Internal Table State

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  //** */
});

console.log(table.getState());
```

##### Console Log Current Rendering Rows

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  //** */
});

console.log(table.getRowModel().rows);
```
